/*
 * SymetricCrypto.cpp
 *
 *  Created on: Sep 15, 2011
 *      Author: Gherman Alin
 */

#include "SymetricCrypto.h"

#include <openssl/aes.h>
#include <openssl/evp.h>


namespace Crypto {

/**
 * key and iv must be 256 bit long (= unsigned char[32])
 * salt must be unsigned char[8] or NULL for no salt
 */
void my_aes_key_iv_init(const unsigned char * passData, int passLen,
		const unsigned char *salt, unsigned char *key, unsigned char *iv) {
	int i, nrounds = 5;
	i = EVP_BytesToKey(EVP_aes_256_cfb128(), EVP_sha512(), salt,
			passData, passLen, nrounds, key, iv);
}

/**
 * key and iv must be 256 bit long (= unsigned char[32])
 * out size must be inLen + EVP_MAX_BLOCK_LENGTH (just to be safe)
 */
int my_aes_encrypt(const unsigned char *key, const unsigned char *iv,
		const unsigned char *in, int inLen, unsigned char *out) {
	int outlen, outlenFin;

	EVP_CIPHER_CTX ctx;
	EVP_CIPHER_CTX_init(&ctx);
	const EVP_CIPHER *cipher = EVP_aes_256_cfb128();

	EVP_EncryptInit(&ctx, cipher, key, iv);
	EVP_EncryptUpdate(&ctx, out, &outlen, in, inLen);
	EVP_EncryptFinal(&ctx, out + outlen, &outlenFin);

	EVP_CIPHER_CTX_cleanup(&ctx);
	return outlen + outlenFin;
}

/**
 * key and iv must be 256 bit long (= unsigned char[32])
 * out size must be inLen (just to be safe)
 */
int my_aes_decrypt(const unsigned char *key, const unsigned char *iv,
		const unsigned char *in, int inLen, unsigned char *out) {
	int outlen, outlenFin;

	EVP_CIPHER_CTX ctx;
	EVP_CIPHER_CTX_init(&ctx);
	const EVP_CIPHER *cipher = EVP_aes_256_cfb128();

	EVP_DecryptInit(&ctx, cipher, key, iv);
	EVP_DecryptUpdate(&ctx, out, &outlen, in, inLen);
	EVP_DecryptFinal(&ctx, out + outlen, &outlenFin);

	EVP_CIPHER_CTX_cleanup(&ctx);
	return outlen + outlenFin;
}

SymetricCrypto::SymetricCrypto() {
}

SymetricCrypto::SymetricCrypto(const std::string& pass) {
	setPassword(pass);
}

SymetricCrypto::~SymetricCrypto() {
}

void SymetricCrypto::setPassword(const std::string& pass) {
	static const unsigned char salt[8] = { 2, 70, 9, 123, 38, 90, 199, 210 };
	const unsigned char *passData = (unsigned char *) pass.c_str();
	int passLen = pass.length();
	my_aes_key_iv_init(passData, passLen, salt, key, iv);
}

void SymetricCrypto::encrypt(std::string& inStr, std::string& outStr) {
	const unsigned char* in = (unsigned char*) inStr.c_str();
	int inLen = inStr.length();

	unsigned char* out = new unsigned char[inLen + EVP_MAX_BLOCK_LENGTH];
	int outLen = my_aes_encrypt(key, iv, in, inLen, out);
	outStr.assign((char *) out, outLen);
	delete[] out;
}

void SymetricCrypto::decrypt(std::string& inStr, std::string& outStr) {
	const unsigned char* in = (unsigned char*) inStr.c_str();
	int inLen = inStr.length();

	unsigned char* out = new unsigned char[inLen];
	int outLen = my_aes_decrypt(key, iv, in, inLen, out);
	outStr.assign((char *) out, outLen);
	delete[] out;
}

}
